<?php
/**
 * Created by PhpStorm.
 * User: bobo
 * Date: 18-03-20
 * Time: 上午11:19
 */

namespace WsdServer\Lib\PDO;
// Comment by hb on 2018-03-19: namespace 不能包含 Mysql，否则wrapperClass无法识别，真奇妙？！
//namespace WsdServer\Lib\MySql;

use Doctrine\Common\EventManager;

use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Cache\QueryCacheProfile;

use Doctrine\DBAL\Connection AS Connection;


/**
 * A wrapper around a Doctrine\DBAL\Connection that adds features like
 * reconnect
 *
 * @author hb  <huangbo@wanglibank.com> - 2018/03/20
 */
class WsdConnection extends Connection
{
    const RECONNECT_MAX_TIMES = 3;  // 最多重试次数

    private $reconnectRetryTimes;

    public function __construct(array $params, Driver $driver, Configuration $config = null,
                                EventManager $eventManager = null)
    {
        parent::__construct($params, $driver, $config, $eventManager);
    }

    /**
     * executeQuery - 支持自动重连机制的封装
     * Add by hb on 2018/03/20
     *
     * Executes an, optionally parametrized, SQL query.
     *
     * If the query is parametrized, a prepared statement is used.
     * If an SQLLogger is configured, the execution is logged.
     *
     * @param string $query The SQL query to execute.
     * @param array $params The parameters to bind to the query, if any.
     * @param array $types The types the previous parameters are in.
     * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp The query cache profile, optional.
     *
     * @return \Doctrine\DBAL\Driver\Statement The executed statement.
     *
     * @throws DBALException
     * @throws \Exception
     */
    public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null)
    {
        try {
            $result = parent::executeQuery($query, $params, $types, $qcp);
            $this->reconnectRetryTimes = 0;
            return $result;
        } catch (DBALException $dex){
            if ( $dex->getErrorCode() == 2006 ) {
                if ($this->reconnectRetryTimes <= WsdConnection::RECONNECT_MAX_TIMES) {
                    $this->reconnectRetryTimes++;
                    secho("ORM-executeQuery", "MySQL Reconnect...("
                        . $this->reconnectRetryTimes . "/" . WsdConnection::RECONNECT_MAX_TIMES . ")");
                    $this->close();
                    return $this->executeQuery($query, $params, $types, $qcp);
                }
            }
            throw $dex;
        } catch (\Exception $ex) {
            throw $ex;
        }
    }


    /**
     * executeUpdate - 支持自动重连机制的封装
     * Add by hb on 2018/03/20
     *
     * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
     * and returns the number of affected rows.
     *
     * This method supports PDO binding types as well as DBAL mapping types.
     *
     * @param string $query The SQL query.
     * @param array $params The query parameters.
     * @param array $types The parameter types.
     *
     * @return integer The number of affected rows.
     *
     * @throws DBALException
     * @throws \Exception
     */
    public function executeUpdate($query, array $params = array(), array $types = array())
    {
        try {
            $result = parent::executeUpdate($query, $params, $types);
            $this->reconnectRetryTimes = 0;
            return $result;
        } catch (DBALException $dex){
            if ( $dex->getErrorCode() == 2006 ) {
                $this->reconnectRetryTimes++;
                if ($this->reconnectRetryTimes <= WsdConnection::RECONNECT_MAX_TIMES) {
                    secho("ORM-executeQuery", "MySQL Reconnect...("
                        . $this->reconnectRetryTimes . "/" . WsdConnection::RECONNECT_MAX_TIMES . ")");
                    $this->close();
                    parent::executeUpdate($query, $params, $types);
                }
            }
            throw $dex;
        } catch (\Exception $ex) {
            throw $ex;
        }
    }

}
